/**
 * @file inotifyer.cpp
 * @author wanghm
 * @brief 
 * @version 0.1
 * @date 2022-01-28
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include "inotifyer.h"

#include <algorithm>

#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>

#include "ioloop.h"

using namespace std;

#define INOTIFY_BUF_SIZE 1024

namespace tnet
{
	
    Inotifyer::Inotifyer(const std::string& path,int notify_option, const InotifyerHandler_t& handler)
        : m_loop(0)
        , m_fd(-1)
        , m_running(false)
        , m_path{path}
        , m_handler(handler)
    {
        m_fd = Inotifyer::createInotifyFd(path,notify_option,m_result);
    }

    Inotifyer::~Inotifyer()
    {
        LOG_INFO("destroyed %d", m_fd);
        if(m_fd > 0)
        {
            inotify_rm_watch(m_fd,m_result);
        } 
    } 
  
    int Inotifyer::createInotifyFd(const std::string& path,int notify_option,int& result)
    {
        int fd = inotify_init();
        if(fd < 0)
        {
            LOG_ERROR("notifyFd error %s", errorMsg(errno));    
            return -1;
        }
        result = inotify_add_watch(fd,path.c_str(),notify_option);
        if(result == -1)
        {
            LOG_ERROR("inotify_add_watch error:%s",errorMsg(errno));
            return -1;
        }   
        return fd;
    }
   
    void Inotifyer::start(IOLoop* loop)
    {
        assert(m_fd > 0);
        if(m_running)
        {
            LOG_WARN("Inotifyer was started");
            return;    
        }
        
        LOG_INFO("start Inotifyer %d", m_fd);
        
        m_running = true;
        m_loop = loop;

        m_loop->addHandler(m_fd, TNET_READ, 
            std::bind(&Inotifyer::onInotify, shared_from_this(), _1, _2));
    }
    
    void Inotifyer::stop()
    {
        assert(m_fd > 0);
        if(!m_running)
        {
            LOG_WARN("Inotifyer was stopped");
            return;    
        }

        LOG_INFO("stop Inotifyer %d", m_fd);

        m_running = false;

        m_loop->removeHandler(m_fd);
    }     

    void Inotifyer::onInotify(IOLoop* loop, int events)
    {
        InotifyerPtr_t Inotifyer = shared_from_this();
        
        char buf[INOTIFY_BUF_SIZE]={0};
        struct inotify_event *event;
        char* p;

        int len = read(m_fd,buf,INOTIFY_BUF_SIZE);
        for(p = buf ; p < buf + len;)
        {
            event = (struct inotify_event *)(p);
            m_handler(Inotifyer, event->name,event->mask);
            p += sizeof(struct inotify_event) + event->len;
        }
    }
}
